package logic

import (
	"delayer/utils"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"time"
)

// 定时器类
type Timer struct {
	Config      utils.Config
	Logger      utils.Logger
	Ticker      *time.Ticker
	Pool        *redis.Pool
	HandleError func(err error, funcName string, data string)
}

const (
	KEY_JOB_POOL       = "delayer:job_pool"
	PREFIX_JOB_BUCKET  = "delayer:job_bucket:"
	PREFIX_READY_QUEUE = "delayer:ready_queue:"
)

// 初始化
func (p *Timer) Init() {
	pool := &redis.Pool{
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", p.Config.Redis.Host+":"+p.Config.Redis.Port)
			if err != nil {
				return nil, err
			}
			if (p.Config.Redis.Password != "") {
				if _, err := c.Do("AUTH", p.Config.Redis.Password); err != nil {
					c.Close()
					return nil, err
				}
			}
			if _, err := c.Do("SELECT", p.Config.Redis.Database); err != nil {
				c.Close()
				return nil, err
			}
			return c, nil
		},
		MaxIdle:         p.Config.Redis.MaxIdle,
		MaxActive:       p.Config.Redis.MaxActive,
		IdleTimeout:     time.Duration(p.Config.Redis.IdleTimeout) * time.Second,
		MaxConnLifetime: time.Duration(p.Config.Redis.ConnMaxLifetime) * time.Second,
	}
	p.Pool = pool
	handleError := func(err error, funcName string, data string) {
		if (err != nil) {
			if (data != "") {
				data = ", [" + data + "]"
			}
			p.Logger.Error(fmt.Sprintf("FAILURE: func %s, %s%s.", funcName, err.Error(), data), false)
		}
	}
	p.HandleError = handleError
}

// 开始
func (p *Timer) Start() {
	ticker := time.NewTicker(time.Duration(p.Config.Delayer.TimerInterval) * time.Millisecond)
	go func() {
		for range ticker.C {
			p.run()
		}
	}()
	p.Ticker = ticker
}

// 执行子任务
func (p *Timer) run() {
	// 获取到期的任务
	jobs, err := p.getExpireJobs()
	if (err != nil) {
		p.HandleError(err, "getExpireJobs", "")
		return
	}
	// 并行获取Topic
	topics := make(map[string][]string)
	ch := make(chan []string)
	//根据过期任务的数量,并发获取任务标题
	for _, jobID := range jobs {
		go p.getJobTopic(jobID, ch)
	}
	// 从通道取出任务ID和任务标题,并按相同标题进行分组
	for i := 0; i < len(jobs); i++ {
		arr := <-ch
		//如果任务标题不为空
		if (arr[1] != "") {
			//从map中获取以任务标题为key的值
			if _, ok := topics[arr[1]]; !ok {
				//如果不存在该key,就新建
				jobIDs := []string{arr[0]}
				topics[arr[1]] = jobIDs
			} else {
				//如果已存在,就追加任务ID到key
				topics[arr[1]] = append(topics[arr[1]], arr[0])
			}
		}
	}
	// 轮询任务的最后,将过期的任务移动到只读队列
	for topic, jobIDs := range topics {
		go p.moveJobToReadyQueue(topic,jobIDs)
	}
}

// 执行
func (p *Timer) Stop() {
	p.Ticker.Stop()
}
